import hmac
import json
import requests
import urllib.parse as urlparse
import hashlib
import sys
import time
import base64

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class Side(Enum):
  BUY = "buy"
  SELL = "sell"

class Type(Enum):
  LIMIT = "limit"
  MARKET = "market"

class TimeInForce(Enum):
  GTC = "gtc"
  PO = "po"
  IOC = "ioc"
  FOK = "fok"

class GopaxAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _gen_signature(self, timestamp: str, method: str, path: str, body: str = None):
    assert timestamp, "timestamp must be not None and not empty!"
    assert method, "method must be not None and not empty!"
    assert path, "path must be not None and not empty!"

    before_signature = 't' + ''.join(filter(None, [timestamp, method, path, body]))
    secret = base64.b64decode(self._key.secret_key)

    signature = hmac.new(secret, before_signature.encode(), digestmod=hashlib.sha512)
    return base64.b64encode(signature.digest())

  def __call__(self, req: requests.PreparedRequest):
    timestamp = str(int(time.time() * 1000))
    method = req.method
    path = urlparse.urlparse(req.url).path
    body = req.body and req.body.decode()
    signature = self._gen_signature(timestamp, method, path, body)

    req.headers.update({'api-key': self._key.access_key})
    req.headers.update({'timestamp': timestamp})
    req.headers.update({'signature': signature})
    req.prepare_headers(req.headers)

    # print(req.headers)
    # print(req.body)
    # print(req.url)

    return req

class GopaxPrivateClient():
  def __init__(self, key_path: str, ip=None):
    self._auth = GopaxAuth(key_path)
    self._url = "https://api.gopax.co.kr"

    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def get_open_order(self, include_past: bool = None, trading_pair_name: str = None, status: str = None):
    method = "GET"
    json = {
      'includePast': include_past,
      'tradingPairName': trading_pair_name,
      'status': status,
    }
    path = "/orders"

    response = self._query.query(method=method, path=path, json=json)
    return response.json()

  def cancel_order(self, order_id: str = None, client_order_id: str = None):
    assert not (order_id and client_order_id), "can not set both order_id and client_order_id!"
    
    method = "DELETE"
    path = ''
    if order_id:
      path = "/orders/{}".format(order_id)
    elif client_order_id:
      path = "/orders/clientOrderId/{}".format(client_order_id)
    else:
      assert False, "order_id and client_order_id at least set one!"

    response = self._query.query(method=method, path=path)
    return response.json()

  def submit_order(
    self,
    trading_pair_name: str,
    side: Side,
    type: Type,
    price: str,
    amount: str,
    client_order_id: str = None,
    stop_price: str = None,
    time_in_force: TimeInForce = None,
  ):
    assert trading_pair_name, "trading_pair_name must be not None and not empty!"
    assert side, "side must be not None!"
    assert type, "type must be not None!"
    assert price, "price must be not None and not empty!"
    assert amount, "amount must be not None and not empty!"
    
    method = "POST"
    path = '/orders'
    json = {
      "clientOrderId": client_order_id,
      "tradingPairName": trading_pair_name,
      "side": side.value,
      "type": type.value,
      "price": price,
      "stopPrice": stop_price,
      "amount": amount,
      "timeInForce": time_in_force and time_in_force.value,
    }

    response = self._query.query(method=method, path=path, json=json)
    return response.json()

if __name__ == "__main__":
  key_path = sys.argv[1]
  
  gopax = GopaxPrivateClient(key_path)
  info = gopax.get_open_order()
  # info = gopax.cancel_order(client_order_id="1234")
  # info = gopax.submit_order("BTC-USDC", Side.SELL, Type.LIMIT, "100000000", "0.00000001")
  print(json.dumps(info, indent=2))
